#ifndef __IM_OBJECT_H__
#define __IM_OBJECT_H__

#include <im_base_types.h>
#include <im_quark.h>
#include <im_type.h>

/**
 * @defgroup IMObject
 * @ingroup PublicIME
 * @{
 */

/** @struct IMObjectClass
 *  @brief  The base struct for all classes that derived from IM_TYPE_OBJECT.
 *
 *  All classes which inherit IM_TYPE_OBJECT should have its own
 *  object class struct which contains IMObjectClass as the first member.
 *
 *  IMObjectClass holds all virtual functions that derived class should
 *  implement:
 *  - copy          method to copy the content of an object to another.
 *  - serialize     serialize the content of an object into a IMStream.
 *  - deserialize   deserialize an object from a IMStream.
 */

/** @struct IMObject
 *  @brief  The base struct for all object instances.
 *
 *  All objects which inherit IM_TYPE_OBJECT should have its own
 *  object struct which contains IMObject as the first member.
 *
 *  Newly created IMObject instance will have initial reference count set to -1,
 *  indicating that this instance is floating and not owned by anyone..
 */

/** @struct IMInitiallyOwnedClass
 *  @brief  The base struct for all classes that derived from
 *  IM_TYPE_INITIALLY_OWNED.
 *
 *  All classes which inherit IM_TYPE_INITIALLY_OWNED should have its own
 *  object class struct which contains IMInitiallyOwnedClass as the first
 *  member.
 *
 *  Unlike IMObject, objects of InitiallyOwnedClass are not floating initially.
 *  In another word, their reference count will be set to 1 to indiate that
 *  they are owned by their creators.
 */

/** @struct IMInitiallyOwned
 *  @brief  The base struct for all initially owned object instances.
 *
 *  All objects which inherit IM_TYPE_INITIALLY_OWNED should have its own
 *  object struct which contains IMInitiallyOwned as the first member.
 *
 *  Objects that are not transferable (can not be copied or serialized),
 *  shall be derived from IM_TYPE_INITIALLY_OWNED.
 */


IM_BEGIN_DECLS

/*******************< type macros for IMObject >****************/
#define IM_OBJECT(object)           (IM_TYPE_INSTANCE_CHECK_CAST((object), IM_TYPE_OBJECT, IMObject))
#define IM_CONST_OBJECT(object)     (IM_TYPE_INSTANCE_CHECK_CAST_CONST((object), IM_TYPE_OBJECT, IMObject))
#define IM_OBJECT_CLASS(klass)      (IM_TYPE_CLASS_CHECK_CAST((klass), IM_TYPE_OBJECT, IMObjectClass))
#define IM_IS_OBJECT(object)        (IM_TYPE_INSTANCE_CHECK_TYPE((object),IM_TYPE_OBJECT))
#define IM_IS_OBJECT_CLASS(klass)   (IM_TYPE_CLASS_CHECK_TYPE((klass),IM_TYPE_OBJECT))
#define IM_OBJECT_GET_CLASS(object) (IM_TYPE_INSTANCE_GET_CLASS_CAST((object),IM_TYPE_OBJECT,IMObjectClass))

#define IM_TYPE_IS_OBJECT(type)     (im_type_is_a((type),IM_TYPE_OBJECT))

/*******************< type macros for IMInitiallyOwned >****************/
#define IM_INITIALLY_OWNED(object) \
  (IM_TYPE_INSTANCE_CHECK_CAST((object), IM_TYPE_INITIALLY_OWNED, IMInitiallyOwned))
#define IM_CONST_INITIALLY_OWNED(object) \
  (IM_TYPE_INSTANCE_CHECK_CAST_CONST((object), IM_TYPE_INITIALLY_OWNED, IMInitiallyOwned))
#define IM_INITIALLY_OWNED_CLASS(klass) \
  (IM_TYPE_CLASS_CHECK_CAST((klass), IM_TYPE_INITIALLY_OWNED, IMInitiallyOwnedClass))
#define IM_IS_INITIALLY_OWNED(object) \
  (IM_TYPE_INSTANCE_CHECK_TYPE((object),IM_TYPE_INITIALLY_OWNED))
#define IM_IS_INITIALLY_OWNED_CLASS(klass) \
  (IM_TYPE_CLASS_CHECK_TYPE((klass),IM_TYPE_INITIALLY_OWNED))
#define IM_INITIALLY_OWNED_GET_CLASS(object) \
  (IM_TYPE_INSTANCE_GET_CLASS_CAST((object),IM_TYPE_INITIALLY_OWNED,IMInitiallyOwnedClass))

#define IM_TYPE_IS_INITIALLY_OWNED(type) \
  (im_type_is_a((type),IM_TYPE_IS_INITIALLY_OWNED))

typedef struct _IMObject                IMObject;
typedef struct _IMObjectClass           IMObjectClass;
typedef struct _IMObject                IMInitiallyOwned;
typedef struct _IMObjectClass           IMInitiallyOwnedClass;
typedef struct _IMObjectImpl            IMObjectImpl;

struct _IMObjectClass
{
    IMTypeClass          parent_class;

    /**< public virtual method >**/

    /**< Copy the content of src object to dest object. >**/
    void                (*copy)         (IMPointer dest, IMConstPointer src);
    /**< Save the object into an IMStream object. >**/
    IMBool              (*serialize)    (IMConstPointer obj, IMPointer stream);
    /**< Load the object from an IMStream object. >**/
    IMBool              (*deserialize)  (IMPointer obj, IMPointer stream);
};

struct _IMObject
{
    IMTypeInstance       parent_instance;

    /**< private >**/
    IMObjectImpl        *impl;         /**< Implementation data >**/
};

/**
 * @brief Get parent class pointer of a class which is derived from
 * IMObjectClass.
 */
const IMObjectClass *im_object_class_get_parent (IMConstPointer     klass);

/**
 * @brief Create a new object instance for a given type.
 *
 * This function will allocate required memory and call constructors of
 * the given class type and its parent class types in correct sequence
 * to initialize the object.
 *
 * A newly created object doesn't have reference count, it's called
 * floating object. It can be destroyed by calling im_object_unref().
 *
 * @param type  Type of the class.
 * @return A pointer to the newly created object.
 */
IMPointer       im_object_new               (IMType              type);

/**
 * @brief Increate reference count of an object by one.
 *
 * For a newly created object, the reference count will be set to 1.
 * Means that it now has one owner.
 *
 * @param object Pointer to the object.
 * @return The same pointer if success, 0 if failed.
 */
IMPointer       im_object_ref               (IMPointer           object);

/**
 * @brief Decrease reference count of an object by one.
 *
 * For a newly created object, it'll be destroyed directly.
 * For object which has reference count, it'll only be destroyed
 * when reference count is reduced to zero.
 *
 * @param object Pointer to the object.
 */
void            im_object_unref             (IMPointer           object);

/**
 * @brief Check if a specified object is currently floating.
 *
 * A floating object is not owned by any other object. The reference count of
 * a floating object is -1.
 * Unreferencing a floating object will destroy it directly.
 * Referencing a floating object will increase its reference count to 1, to
 * indicate that it's currently owned by someone.
 */
IMBool          im_object_is_floating       (IMPointer           object);

/**
 * @brief Turn a specified object into floating mode, if it's not floating.
 *
 * Only objects with one owner (reference count equals to 1) shall be
 * turned into floating mode again.
 */
void            im_object_force_floating    (IMPointer           object);

/**
 * @brief Copy the content of src object to dest object.
 *
 * The copy function is a virtual function. Each class should implement
 * its own copy function with correct behaviour.
 *
 * This function will only call copy method of dest class. Copy method of
 * its parent classes should be called inside copy method of dest class.
 *
 * The type of src and dest must be same or at least src is a derived type of
 * dest. Otherwise the copy will fail.
 *
 * @param dest Pointer to dest object.
 * @param src  Pointer to source object.
 * @return dest on success, 0 on fail.
 */
IMPointer       im_object_copy              (IMPointer           dest,
                                             IMConstPointer      src);

/**
 * @brief Clone an object.
 *
 * It creates a new object and copy the content from source object by
 * calling im_object_copy ().
 *
 * @param object The source object to be cloned.
 * @return New cloned object.
 */
IMPointer       im_object_clone             (IMConstPointer      object);

/**
 * @brief Return the size of an object.
 * @param object The object to be checked.
 */
IMSize          im_object_sizeof            (IMConstPointer      object);

/**
 * @brief Check whether an object is valid or not.
 * @param object The object to be checked.
 */
IMBool          im_object_valid             (IMConstPointer      object);

/**
 * @brief Get the type of an object.
 *
 * This is simply a convenience function for im_type_instance_get_type().
 *
 * @param object The object to be checked.
 */
IMType          im_object_get_type          (IMConstPointer      object);

/**
 * @brief Get class struct pointer of an object.
 * @param object The object to be checked.
 */
const IMObjectClass *im_object_get_class    (IMConstPointer      object);

/**
 * @brief Check whether an object is an instance of a type.
 *
 * This is a convenience function for im_type_instance_is_a().
 *
 * @param object The object to be checked.
 * @param is_a_type The type to be checked against.
 */
IMBool          im_object_is_a              (IMConstPointer      object,
                                             IMType              is_a_type);
/**
 * @brief Save an object into an IMStream object.
 *
 * This function will only call serialize method of the object's class.
 *
 * serialize method of its parent class should be called inside its own
 * serialize method, so that data member of its parent class can be
 * serialized correctly.
 *
 * This function will not write type information of the object into
 * the stream. Use im_stream_put_object() instead to write type information
 * as well as object data into the stream.
 *
 * @param object The object to be serialized.
 * @param stream The stream to serialize object into.
 */
IMBool          im_object_serialize         (IMConstPointer      object,
                                             IMPointer           stream);

/**
 * @brief Load an object from an IMStream object.
 *
 * This function will only call deserialize method of the object's class.
 *
 * deserialize method of its parent class should be called inside its own
 * deserialize method, so that data member of its parent class can be
 * deserialized correctly.
 *
 * This function couldn't handle type information of the object written by
 * im_stream_put_object() function. Use im_stream_get_object() instead to
 * load an object which is written by im_stream_put_object().
 *
 * @param object The object to deserialize into.
 * @param stream The stream to deserialize object from.
 */
IMBool          im_object_deserialize       (IMPointer           object,
                                             IMPointer           stream);

/**
 * @brief Save an object into an IMStream object.
 *
 * This function calls im_stream_put_object(stream, object) to save
 * an object into a stream.
 *
 * @sa im_stream_put_object();
 *
 * @param object The object to be put into an IMStream.
 * @param stream The IMStream to put object into.
 */
IMBool          im_object_put_to_stream     (IMConstPointer      object,
                                             IMPointer           stream);

/**
 * @brief Load an object from an IMStream object.
 *
 * This function calls im_stream_get_object(stream, object) to load
 * an object from a stream.
 *
 * The type of object must be same as the object type in the stream's
 * current read position.
 *
 * The old content of given object will be destroyed.
 *
 * @sa im_stream_get_object();
 *
 * @param object The object to load into.
 * @param stream The stream to get object from.
 *
 * @return TRUE if an object is loaded successfully.
 */
IMBool          im_object_get_from_stream   (IMPointer           object,
                                             IMPointer           stream);
/**
 * @brief Attach an object to another object with specified string key.
 *
 * An object can have one or more objects attached. Each attached
 * object is associated with a specified key, so that it can be
 * retrieved by the key later.
 *
 * @param object The parent object.
 * @param key The string key to be associated to the attached object. It shall
 *            not start with dot ('.').
 * @param att The attached  object. It'll just be referenced by the
 *        parent object, rather than making a copy.
 */
void            im_object_set_attachment    (IMPointer           object,
                                             const IMChar       *key,
                                             IMPointer           att);
/**
 * @brief Return an attached object with specified key of an object.
 *
 * @param object The parent object.
 * @param key Key of the attached object to be retrieved.
 * @return The attached object, or NULL if there is no object for that
 *         key.
 */
IMPointer       im_object_get_attachment    (IMConstPointer      object,
                                             const IMChar       *key);
/**
 * @brief Remove a attached object with specified key of an object.
 *
 * @param object The parent object.
 * @param key Key of the attached object to be removed.
 */
void            im_object_remove_attachment (IMPointer           object,
                                             const IMChar       *key);
/**
 * @brief Attach an object to another object with specified quark key.
 *
 * Just like im_object_set_attachment (), but use a IMQuark instead of
 * a string key.
 *
 * @param object The parent object.
 * @param quark The IMQuark to be associated to the attached object.
 * @param att   The object to be attached. It'll just be referenced by the
 *        parent object, rather than making a copy.
 */
void            im_object_set_quark_attachment(IMPointer           object,
                                               IMQuark             quark,
                                               IMPointer           att);

/**
 * @brief Return the attached object with specified quark key of an object.
 *
 * Just like im_object_get_attachment(), but use a IMQuark instead of a
 * string key.
 *
 * @param object The parent object.
 * @param quark IMQuark of the attached object to be retrieved.
 * @return The attached object, or NULL if there is no object for that
 *         key.
 */
IMPointer       im_object_get_quark_attachment(IMConstPointer      object,
                                               IMQuark             quark);

/**
 * @brief Remove an attached object with specified quark key of an object.
 *
 * Just like im_object_remove_attachment(), but use a IMQuark instead.
 *
 * @param object The parent object.
 * @param quark IMQuark of the attached object to be removed.
 */
void            im_object_remove_quark_attachment(IMPointer           object,
                                                  IMQuark             quark);
IM_END_DECLS

#endif
/** @} */
/*
vi:ts=4:nowrap:ai:expandtab
*/
